package hu.mapro.gwtui.gxt.client.browser;

import hu.mapro.gwtui.client.Action;
import hu.mapro.gwtui.client.browser.Browser;
import hu.mapro.gwtui.client.browser.BuildsColumn;
import hu.mapro.gwtui.client.browser.ColumnBuilder;
import hu.mapro.gwtui.client.browser.Loader;
import hu.mapro.gwtui.client.model.CellSizer;
import hu.mapro.gwtui.client.model.DisplaySize;
import hu.mapro.gwtui.client.widget.Command;
import hu.mapro.gwtui.gxt.client.GxtUiUtils;
import hu.mapro.gwtui.gxt.client.data.GxtDataUtils;
import hu.mapro.model.Getter;

import java.util.List;

import com.google.common.collect.Lists;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.SortDir;
import com.sencha.gxt.data.shared.SortInfo;
import com.sencha.gxt.data.shared.SortInfoBean;
import com.sencha.gxt.data.shared.loader.LoadEvent;
import com.sencha.gxt.data.shared.loader.LoadHandler;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.data.shared.loader.RequestFactoryProxy;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.ViewReadyEvent;
import com.sencha.gxt.widget.core.client.event.ViewReadyEvent.ViewReadyHandler;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar;
import com.sencha.gxt.widget.core.client.toolbar.SeparatorToolItem;

public class GxtBrowserWidget<T> implements
		Browser<T> {

	ModelKeyProvider<T> keyProvider;

	List<ColumnConfig<T, ?>> columns = Lists.newArrayList();
	List<Widget> toolBarItems = Lists.newArrayList();
	List<Action> selectionChangeActions = Lists.newArrayList();
	List<SortInfo> sort = Lists.newArrayList();

	Widget widget = null;

	private RequestFactoryProxy<PagingLoadConfig, PagingLoadResult<T>> proxy;

	private Grid<T> grid;

	public void setKeyProvider(ModelKeyProvider<T> keyProvider) {
		this.keyProvider = keyProvider;
	}

	@Override
	public void addToolBarItem(Widget element) {
		toolBarItems.add(element);
	}

	@Override
	public boolean addSelectionChangedAction(Action e) {
		return selectionChangeActions.add(e);
	}

	@Override
	public Widget getWidget() {
		if (widget == null) {
			widget = createWidget();
		}
		return widget;
	}

	public Widget createWidget() {

		final PagingLoader<PagingLoadConfig, PagingLoadResult<T>> loader = new PagingLoader<PagingLoadConfig, PagingLoadResult<T>>(
				proxy);
		loader.setRemoteSort(true);

		ListStore<T> store = new ListStore<T>(keyProvider);
		loader.addLoadHandler(new LoadResultListStoreBinding<PagingLoadConfig, T, PagingLoadResult<T>>(
				store));

		final PagingToolBar toolBar = new PagingToolBar(50);
		toolBar.getElement().getStyle().setProperty("borderTop", "none");
		toolBar.bind(loader);

		ColumnModel<T> cm = new ColumnModel<T>(columns);

		for (SortInfo s : sort) {
			loader.addSortInfo(s);
		}
		
		
		grid = new Grid<T>(store, cm);
		
		grid.addAttachHandler(new Handler() {
			@Override
			public void onAttachOrDetach(AttachEvent event) {
				if (event.isAttached()) {
					grid.getView().refresh(true);
				}
			}
		});
		
		grid.addViewReadyHandler(new ViewReadyHandler() {
			@Override
			public void onViewReady(ViewReadyEvent event) {
				grid.getView().refresh(true);
			}
		});

		grid.getView().setForceFit(true);
		grid.setLoadMask(true);
		grid.setLoader(loader);
		

		if (!toolBarItems.isEmpty()) {
			toolBar.add(new SeparatorToolItem());

			for (Widget w : toolBarItems) {
				toolBar.add(w);
			}
		}

		final Action updateSelection = new Action() {
			@Override
			public void perform() {
				for (Action a : selectionChangeActions) {
					a.perform();
				}
			}
		};

		loader.addLoadHandler(new LoadHandler<PagingLoadConfig, PagingLoadResult<T>>() {
			@Override
			public void onLoad(
					LoadEvent<PagingLoadConfig, PagingLoadResult<T>> event) {
				updateSelection.perform();
			}
		});

		updateSelection.perform();

		grid.getSelectionModel().addSelectionChangedHandler(
				new SelectionChangedHandler<T>() {
					@Override
					public void onSelectionChanged(
							SelectionChangedEvent<T> event) {
						updateSelection.perform();
					}
				});

		VerticalLayoutContainer con = new VerticalLayoutContainer();
		con.setBorders(true);
		con.add(toolBar, new VerticalLayoutData(1, -1));
		con.add(grid, new VerticalLayoutData(1, 1));

		return con;
	}

	@Override
	public void setIdField(final Getter<T, String> field) {
		keyProvider = new ModelKeyProvider<T>() {
			@Override
			public String getKey(T item) {
				return field.get(item);
			}
		};
	}

	@Override
	public void setLoader(final Loader<T> loader) {
	    proxy = GxtDataUtils.createPagingProxy(loader);		
	}

	static class DelegatedValueProvider<T, D> implements ValueProvider<T, D> {

		protected Getter<T, D> accessor;
		protected String path;

		@Override
		public D getValue(T object) {
			return accessor.get(object);
		}

		@Override
		public void setValue(T object, D value) {
			throw new RuntimeException("setValue not implemented");
		}

		@Override
		public String getPath() {
			return path;
		}
		
	}
	
	@Override
	public <D> ColumnBuilder<T, D> addColumn(BuildsColumn<T, D> bc) {
		final DelegatedValueProvider<T,D> dvp = new DelegatedValueProvider<T,D>();
		final ColumnConfig<T, D> cc = new ColumnConfig<T, D>(dvp);
		columns.add(cc);
		
		ColumnBuilder<T, D> columnBuilder = new ColumnBuilder<T, D>() {

			@Override
			public void setAccessor(Getter<T, D> accessor) {
				dvp.accessor = accessor;
			}

			@Override
			public void setPath(String path) {
				dvp.path = path;
			}

			@Override
			public void setLabel(String label) {
				cc.setHeader(label);
			}

			@Override
			public void setWidth(DisplaySize width) {
				width.set(new CellSizer() {
					@Override
					public void setWidth(int width) {
						cc.setWidth(width);
					}
					
					@Override
					public void setDefault() {
					}
				});
			}

			@SuppressWarnings("unchecked")
			@Override
			public void setCell(Cell<? super D> cell) {
				cc.setCell((Cell<D>) cell);
			}

			@Override
			public void setOrdered(boolean ascending) {
				sort.add(new SortInfoBean(dvp.getPath(), ascending?SortDir.ASC:SortDir.DESC));
			}
			
		};
		
		bc.build(columnBuilder);
		
		return columnBuilder;
	}
	

	@Override
	public List<T> getSelection() {
		return grid.getSelectionModel().getSelection();
	}

	@Override
	public void refresh() {
		grid.getLoader().load();
	}

	@Override
	public Command addToolButton() {
		final TextButton button = new TextButton("<no label>");
		addToolBarItem(button);
		return GxtUiUtils.createCommand(button);
	}



}
